package com.kin207.netty;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.eventbus.EventBus;
import com.kin207.netty.config.CServer;
import com.kin207.netty.config.ServerContext;
import com.kin207.netty.handle.IMessageHandleAdapter;
import com.kin207.netty.handle.adapter.HeadMessageHandleAdapter;
import com.kin207.netty.handle.adapter.HexHeadMessageHandleAdapter;
import com.kin207.netty.listener.Listener;
import com.kin207.netty.listener.eventbus.EventBusListener;
import com.kin207.netty.msg.IMessageParse;
import com.kin207.netty.msg.parse.SplitMessageParse;
import com.kin207.netty.server.IServer;
import com.kin207.netty.server.ServerManager;
import com.kin207.netty.session.SessionManager;
import com.kin207.netty.tcp.TcpChannelHandlerInitializer;
import com.kin207.netty.tcp.TcpChannelInboundHandler;
import com.kin207.netty.tcp.TcpChannelInitializer;
import com.kin207.netty.tcp.TcpServer;
import com.kin207.netty.udp.UdpChannelInboundHandler;
import com.kin207.netty.udp.UdpChannelInitializer;
import com.kin207.netty.udp.UdpServer;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;

public class ServerRun {
	final static Logger logger = LoggerFactory.getLogger(ServerRun.class);
	
	public static void main(String[] args) {
		String cofPath = args.length>0?args[0]:"/conf/server.cnf";
		
		logger.info("load server.cnf:{}", ServerContext.class.getResource(cofPath));
		ServerContext context = ServerContext.create(ServerContext.class.getResourceAsStream(cofPath));
		
		for(CServer item : context.getServers()){
			runServer(item, context);
		}
	}
	
	public static void runServer(CServer cserver, ServerContext context){
		final IServer server = "udp".equals(cserver.getType())?new UdpServer(cserver.getPort()):new TcpServer(cserver.getPort());
		server.setName(cserver.getName());
		if(server.getName()==null || "".equals(server.getName())){
			try {
				server.setName(server.getServerType()+"_"+InetAddress.getLocalHost().getHostAddress()+"_"+server.getPort());
			} catch (UnknownHostException e) {
				logger.error(e.getMessage(), e);
				server.setName(server.getServerType()+"_"+UUID.randomUUID().toString());
			}
		}
		logger.error("server name:{}", server.getName());
		
		//服务消息总线
		EventBus serverEventBus = loadEventBus("server", cserver, context);
		//会话消息总线
		EventBus sessionEventBus = loadEventBus("session", cserver, context);
		//消息消息总线
		EventBus messageEventBus = loadEventBus("message", cserver, context);
		if(server.getEventBus()==null){
			server.setEventBus(serverEventBus);
		}
		
		//服务管理器
		ServerManager serverManager = loadServerManager(server, cserver, context);
		//会话管理器
		SessionManager sessionManager = loadSessionManager(server, cserver, context, sessionEventBus);
		//消息适配器
		IMessageHandleAdapter messageHandleAdapter = loadMessageHandleAdapter(server, cserver, context);
		//消息解析器
		IMessageParse messageParse = loadMessageParse(server, cserver, context);
		//通道初始化
		ChannelInitializer channelInitializer = loadChannelInitializer(server, cserver, context, messageEventBus);
		
		Executors.newSingleThreadExecutor().submit(new Runnable() {
			public void run() {
				logger.info(server.getServerType()+" server run, prot:{}", server.getPort());
				try {
					server.run();
				} catch (InterruptedException e) {
					logger.error(e.getMessage() , e);
				}
			}
		});
	}

	private static ServerManager loadServerManager(IServer server, CServer cserver, ServerContext context){
		ServerManager serverManager = null;
		if(cserver.getServerManager()!=null&&!"".equals(cserver.getServerManager())){
			serverManager = context.getBean(cserver.getServerManager(), ServerManager.class);
		}else{
			serverManager = ServerManager.newInstance();
		}
		server.setServerManager(serverManager);
		return serverManager;
	}
	
	
	private static SessionManager loadSessionManager(IServer server, CServer cserver, ServerContext context, EventBus eventBus){
		SessionManager sessionManager = null;
		if(cserver.getSessionManager()!=null&&!"".equals(cserver.getSessionManager())){
			sessionManager = context.getBean(cserver.getSessionManager(), SessionManager.class);
		}else{
			sessionManager = SessionManager.newInstance();
		}
		if(sessionManager.getEventBus()==null){
			sessionManager.setEventBus(eventBus);
		}
		server.setSessionManager(sessionManager);
		return sessionManager;
	}
	
	private static IMessageHandleAdapter loadMessageHandleAdapter(IServer server, CServer cserver, ServerContext context){
		IMessageHandleAdapter messageHandleAdapter = null;
		if(cserver.getMessageHandleAdapter()!=null && !"".equals(cserver.getMessageHandleAdapter())){
			messageHandleAdapter = context.getBean(cserver.getMessageHandleAdapter(), IMessageHandleAdapter.class);
		}else{
//				messageHandleAdapter = HeadMessageHandleAdapter.newInstance();
			messageHandleAdapter = HexHeadMessageHandleAdapter.newInstance();
		}
		server.setMessageHandleAdapter(messageHandleAdapter);
		return messageHandleAdapter;
	}
	
	
	private static IMessageParse loadMessageParse(IServer server, CServer cserver, ServerContext context){
		IMessageParse messageParse = null;
		if(cserver.getMessageParse()!=null && !"".equals(cserver.getMessageParse())){
			messageParse = context.getBean(cserver.getMessageParse(), SplitMessageParse.class);
		}else{
			messageParse = SplitMessageParse.newInstance();
		}
		server.setMessageParse(messageParse);
		return messageParse;
	}
	
	
	private static ChannelInitializer loadChannelInitializer(IServer server, CServer cserver, ServerContext context, EventBus eventBus){
		ChannelInitializer channelInitializer = null;
		if(cserver.getChannelInitializer()!=null && !"".equals(cserver.getChannelInitializer())){
			channelInitializer = context.getBean(cserver.getChannelInitializer(), ChannelInitializer.class);
		}else{
			EventExecutorGroup eventExecutorGroup = null;
			if(cserver.getMaxThreads()>0){
				logger.info("create DefaultEventExecutorGroup, maxThreads:{}", cserver.getMaxThreads());
				eventExecutorGroup = new DefaultEventExecutorGroup(cserver.getMaxThreads());
			}
			
			if("udp".equals(cserver.getType())){
				channelInitializer = defaultUcpChannelInboundHandler(server, eventBus, eventExecutorGroup);
			}else{
				channelInitializer = defaultTcpChannelInboundHandler(server, eventBus, eventExecutorGroup);
			}
		}
		server.setChannelInitializer(channelInitializer);
		return channelInitializer;
	}
	
	private static EventBus loadEventBus(String type, CServer cserver, ServerContext context){
		EventBus eventBus = null;
		if(cserver.getEventBus()!=null && !"".equals(cserver.getEventBus())){
			eventBus = context.getBean(cserver.getEventBus(), EventBus.class);
		}else{
			List<Listener> listeners = context.getListenerList(type);
			logger.info("loadEventBus getListenerList,type:{}, size:{}", type, listeners==null?"null": listeners.size());
			if(listeners!=null && listeners.size()>0){
				eventBus = new EventBus();
				EventBusListener<Listener> eventBusListener = new EventBusListener();
				for(Listener item : listeners){
					eventBusListener.addListener(item);
				}
				eventBus.register(eventBusListener);
			}
		}
		return eventBus;
	}
	
	
	private static TcpChannelInitializer defaultTcpChannelInboundHandler(IServer server, EventBus eventBus, EventExecutorGroup eventExecutorGroup){
		TcpChannelInboundHandler channelInboundHandler = new TcpChannelInboundHandler();
		channelInboundHandler.setEventBus(eventBus);
		channelInboundHandler.setServer(server);
		
		Map<String, ChannelHandler> channelHandlers = new LinkedHashMap();
		//channelHandlers.put("idleStateHandler", new IdleStateHandler(2, 2, 2, TimeUnit.SECONDS));
		channelHandlers.put("@handler", channelInboundHandler);
		
		TcpChannelInitializer channelInitializer = new TcpChannelInitializer();
		channelInitializer.setInitHandles(channelHandlers);
		channelInitializer.setChannelHandlerInitializer(new TcpChannelHandlerInitializer());
		if(eventExecutorGroup!=null){
			channelInitializer.setEventExecutorGroup(eventExecutorGroup);
		}
		return channelInitializer;
	}
	
	private static UdpChannelInitializer defaultUcpChannelInboundHandler(IServer server,  EventBus eventBus, EventExecutorGroup eventExecutorGroup){
		UdpChannelInboundHandler channelInboundHandler = new UdpChannelInboundHandler();
		channelInboundHandler.setEventBus(eventBus);
		channelInboundHandler.setServer(server);
		
		Map<String, ChannelHandler> channelHandlers = new LinkedHashMap();
		//channelHandlers.put("idleStateHandler", new IdleStateHandler(2, 2, 2, TimeUnit.SECONDS));
		channelHandlers.put("@handler", channelInboundHandler);
		
		UdpChannelInitializer channelInitializer = new UdpChannelInitializer();
		channelInitializer.setInitHandles(channelHandlers);
		if(eventExecutorGroup!=null){
			channelInitializer.setEventExecutorGroup(eventExecutorGroup);
		}
		return channelInitializer;
	}
}
